//    Copyright (C) Kherty.  All rights reserved.
#region

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;

#endregion

namespace OpenLS.Spreadsheet.UI.Internals
{
    abstract class CellTextSourceBase: TextSource , INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        internal void NotifyPropertyChanged(string propertyName)
        {
            Contract.Requires(GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null || propertyName.Contains("."));
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }


        private int _otherIndex;
        private int _selectionIndex;

        public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
        {
            throw new NotSupportedException();
        }

        public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(int textSourceCharacterIndex)
        {
            throw new NotSupportedException();
        }

        protected abstract IEnumerable<CellTextRun> GetRuns();

        public override TextRun GetTextRun(int textSourceCharacterIndex)
        {
            var index = 0;

            foreach (var run in GetRuns())
            {
                if (run.Length + index > textSourceCharacterIndex)
                {
                    CellTextRun validRun;
                    var offset = textSourceCharacterIndex - index;
                    if (offset == 0)
                        validRun = run;
                    else
                    {
                        validRun = new CellTextRun(run.Text.Substring(offset, run.Length - offset), run.Properties);
                    }
                    if (MinIndex < MaxIndex)
                    {
                        int minTextIndex = index + offset;
                        int maxTextIndex = index + run.Length;
                        if (minTextIndex < MaxIndex && maxTextIndex > MinIndex)
                        {
                            if (textSourceCharacterIndex < MinIndex)
                            {
                                validRun = new CellTextRun(validRun.Text.Substring(0, MinIndex - minTextIndex), validRun.Properties);
                            }
                            else if (textSourceCharacterIndex > MaxIndex)
                            {
                                validRun = new CellTextRun(validRun.Text.Substring(MaxIndex - minTextIndex, maxTextIndex - maxTextIndex), validRun.Properties);                                
                            }
                            else
                            {
                                int actualMin = Math.Max(MinIndex, minTextIndex);
                                int actualMax = Math.Min(MaxIndex, maxTextIndex);
                                validRun =
                                    new CellTextRun(
                                        validRun.Text.Substring(actualMin - minTextIndex, actualMax - actualMin),
                                        new CellInheritedTextRunProperties(validRun.Properties,
                                                                           CellTextSource.InvertBrush(
                                                                               validRun.Properties.
                                                                                   ForegroundBrush),
                                                                           validRun.Properties.
                                                                               BackgroundBrush));
                            }
                        }
                    }
                
                    return validRun.GetTextCharacters();
                }
                index += run.Length;
            }
            return new TextEndOfParagraph(1);
        }

        private static Brush InvertBrush (Brush brush)
        {
            SolidColorBrush b = brush as SolidColorBrush;
            if (b == null)
                return brush;
            Color c = b.Color;
            c = Color.FromRgb((byte) (255 - c.R), (byte) (255 - c.G), (byte) (255 - c.B));
            return new SolidColorBrush(c);
        }

        public virtual int SelectionIndex
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return _selectionIndex;
            }
            protected set
            {
                Contract.Requires(value >= 0);
                _selectionIndex = value;
                NotifyPropertyChanged("SelectionIndex");
            }
        }

        public int OtherIndex
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return _otherIndex;
            }
            set
            {
                Contract.Requires(value >= 0);
                _otherIndex = value;
                NotifyPropertyChanged("OtherIndex");
            }
        }

        protected internal int MinIndex
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return Math.Min(SelectionIndex, OtherIndex);
            }
        }

        protected internal int MaxIndex
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return Math.Max(SelectionIndex, OtherIndex);
            }
        }
    }
    internal abstract class CellTextSource : CellTextSourceBase
    {
        public virtual int Length
        {
            get { return Enumerable.Sum (GetRuns(), rr => rr.Length); }
        }

        protected bool IsFlat
        {
            get { return MaxIndex == MinIndex; }
        }

        public abstract string SelectionText { get;  }

        public abstract void OnBackspace();

        public abstract void OnDelete();

        public abstract void OnTextInput(string text);

        public virtual void Select(int index)
        {
            var si = index;
            si = Math.Max(si, 0);
            si = Math.Min(si, Length);
            OtherIndex = si;
        }

        public virtual void Move(int newIndex)
        {
            var si = newIndex;
            si = Math.Max(si, 0);
            si = Math.Min(si, Length);
            SelectionIndex = si;
            OtherIndex = si;
        }

        public abstract void OnToggleLockReference();
    }
}